/**
 * MIT License
 * 
 * Copyright (c) 2021 CaiDongyu
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.axe.spring.cloud.configer;

import java.util.HashSet;
import java.util.Set;

import org.axe.Axe;
import org.axe.helper.ioc.BeanHelper;
import org.axe.util.ClassUtil;
import org.axe.util.LogUtil;
import org.axe.util.ReflectionUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;

@ComponentScan(basePackages = {"org.axe.spring.cloud"})
@Configuration
public class AxeSpringBootInitializer implements BeanPostProcessor,ApplicationListener<ContextRefreshedEvent>{
	 /**
	  * 改变全局拦截器，让spring的mvc替换成axe
	  */
	@Bean
    public FilterRegistrationBean<AxeDispatcherFilter> someFilterRegistration() {
        FilterRegistrationBean<AxeDispatcherFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new AxeDispatcherFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(Integer.MIN_VALUE);
        return registration;
    }
	
	/**
	 * 将spring的bean全部托管到axe
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		 BeanHelper.setBean(bean.getClass(), bean);
		 LogUtil.debug("add bean to axe:"+beanName);
		 return bean;
	}

	/**
	 * 等待spring容器实例化结束，调用axe的初始化，补充spring注入的bean的ioc
	 */
	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		//root application context 没有parent，他就是老大.
		if(event.getApplicationContext().getParent() == null){ 
           initAxe();
        } 
	}
	
	private static Boolean axeInited = false;
	public static void initAxe(){
		//需要执行的逻辑代码，当spring容器初始化完成后就会执行该方法。 
		try {
  			synchronized (AxeSpringConfiger.AXE_CAN_INIT) {
  				if(AxeSpringConfiger.AXE_CAN_INIT) {
  					
  					synchronized (axeInited) {
  						if(axeInited) {
  							LogUtil.error("AxeSpringBootInitializer.init() execute 2 times，this is forbidden! program is shuting down");
  							System.exit(0);
  						}
  						axeInited = true;
  					}
  				
  					//初始化外部组件
  					Set<Class<?>> CLASS_SET = ClassUtil.getClassSet("org.axe.spring.cloud.configer");
  					Set<Class<?>> classSet = new HashSet<>();
  			    	for(Class<?> cls:CLASS_SET){
  			    		if(AxeComponentInitInterface.class.isAssignableFrom(cls) && !AxeComponentInitInterface.class.equals(cls)){
  			    			classSet.add(cls);
  			    		}
  			    	}
  					for(Class<?> cls:classSet) {
  						AxeComponentInitInterface initInterface = ReflectionUtil.newInstance(cls);
  						initInterface.init();
  					}
  					Axe.init();
  					LogUtil.info("axe init success");
  				}
			}
			
		} catch (Exception e) {
			LogUtil.error(e);
			System.exit(0);
		}
	}
}
